var fs = require('fs');
var fsPromises = require('fs/promises');
var path = require('path');
var shpUtil = require('shapefile');
var join = path.join;
var transformService = require("./transformService");

const CommonUtil = require("../utils/CommonUtil");
const { options } = require('../route/transRequest');

//支持shp、geojson格式数据转换
const SUPORT_FILE_TYPE = {
    SHP: 'shp',
    GEOJSON: 'geojson',
    JSON: 'json',
}
const readGeoJSONObjByUrl = async (filePath, projObj) => {
    const fileType = filePath.substring(filePath.lastIndexOf(".")+1);
    const data = (fileType === SUPORT_FILE_TYPE.GEOJSON || fileType === SUPORT_FILE_TYPE.JSON) 
    ? JSON.parse(fs.readFileSync(filePath, 'utf8'))
    : await shpUtil.read(filePath).then(f=>{
        return f;
    });
    return transformData(data, projObj);
}

const readTxtByUrl = (filePath) => {
    const data = fs.readFileSync(filePath, 'utf8');
    const lines = data.split(/\r?\n/);
    const newData = [];
    lines.forEach((line) => {
        if (line) newData.push(line);
    });
    return newData;
}

const createGeoJSON = (datas) => {
    const geojsonstr = {
        "type": "FeatureCollection",
        "features": []
    }
    if (!Array.isArray(datas)) return;

    for (let i = 1; i < datas.length; i++) {
        const d = datas[i];
        const dArr = d.split(",");
        geojsonstr.features.push({
            "type": "Feature",
            "properties": {
                t: +dArr[0],
                x: +dArr[1],
                y: +dArr[2],
                z: +dArr[3]
            },
            "geometry": {
                "type": "Point",
                "coordinates": (dArr[3] || dArr[3] === 0) ? [+dArr[1], +dArr[2], +dArr[3]] : [+dArr[1], +dArr[2]]
            }
        })

    }

    return geojsonstr;
}

const transformData = (geoData, projObj) => {
    const newGeoData = Object.assign({
        features: []
    }, {
        type: geoData.type,
        // crs: geoData.crs,
        name: geoData.name
    })
    const features = geoData["features"];

    features.forEach((feature, k) => {
        const geo = feature["geometry"];
        const props = feature["properties"];
        const newFeature = Object.assign({
            geometry: {}
        },
            {
                type: feature["type"],
                properties: props,
                geometry: filesService.transCoordsByGeo(geo, projObj)
            });

        newGeoData.features.push(newFeature);

    });

    return newGeoData;
}


const filesService = {
    /**
     * 
     * @param {*} fileOptions 
     * @param {*} callback
     */
    getFiles: (fileOptions, callback) => {
        switch (fileOptions.zipType) {
            case "txt":
                filesService.getTxtFiles(fileOptions, callback);
                break;
            case "geojson":
            case "shp":    
                filesService.getGeoFiles(fileOptions, callback);
                break;
            default:
                filesService.getGeoFiles(fileOptions, callback);
                break;
        }
    },
    /**
     * 
     * @param {*} fileOptions 
     * @param {*} callback 
     */
    getTxtFiles: async (fileOptions, callback) => {
        const {filesPath,projObj} = fileOptions;
        let hasAllDataWriteed = false;
        const outputDirPath = join(filesPath, `../${projObj.method || projObj.toWkid || '4326'}`);
        await CommonUtil.mkdirFiles(outputDirPath);

        function findJsonFile(path) {
            let files = fs.readdirSync(path);
            const geojsonFiles = files.filter((f) => {
                let fPath = join(path, f);
                let stat = fs.statSync(fPath);
                if (stat.isFile() === true && f.substring(f.lastIndexOf(".")+1) === "txt") {
                    return f;
                }
            });
            geojsonFiles.forEach(function (item, index) {
                let fPath = join(path, item);
                const txtLines = readTxtByUrl(fPath);
                const geoData = createGeoJSON(txtLines);
                const transedGeoData = transformData(geoData, projObj);
                const geoFilePath = join(outputDirPath, `/${item.substring(0, item.lastIndexOf("."))}.geojson`);

                fs.writeFileSync(geoFilePath, JSON.stringify(transedGeoData, "", "\t"), { encoding: 'utf-8' });
                if (index === geojsonFiles.length - 1) {
                    hasAllDataWriteed = true;
                    callback && callback(hasAllDataWriteed, outputDirPath);
                }
            });

            !hasAllDataWriteed && callback(hasAllDataWriteed, outputDirPath, "转换失败，请检查压缩包文件，压缩子文件是否txt文件");

        }
        findJsonFile(filesPath);
    },
    /**
     * // 集成geojson、shp格式数据
     * @param {*} fileOptions {filesPath,projObj,outpath}
     * @param {*} callback 
     * @returns 
     */
    getGeoFiles: async (fileOptions, callback)=> {
        let hasAllDataWriteed = false;
        const {outpath,filesPath,projObj} = fileOptions;
        try {
            const that = this;
            const outputDirPath = outpath || join(filesPath, `../${projObj.method || projObj.toWkid || '4326'}`);
            await CommonUtil.mkdirFiles(outputDirPath);
            function findJsonFile(path) {
                let files = fs.readdirSync(path);
                const geojsonFiles = files.filter((f) => {
                    let fPath = join(path, f);
                    let stat = fs.statSync(fPath);
                    if (stat.isFile() === true && (f.substring(f.lastIndexOf(".")+1) === SUPORT_FILE_TYPE.GEOJSON || f.substring(f.lastIndexOf(".")+1) === SUPORT_FILE_TYPE.SHP)) {
                        return f;
                    }
                });

                !geojsonFiles.length && callback(hasAllDataWriteed, outputDirPath, "转换失败，请检查子文件是否geojson、shp文件");

                let writeFileCount = 0;
                geojsonFiles.length && geojsonFiles.forEach(function (item, index) {
                    ((i)=>{
                        let fPath = join(path, item);
                        readGeoJSONObjByUrl(fPath, projObj).then(geoData=>{
                            const geoFilePath = join(outputDirPath, `/${item.substring(0, item.lastIndexOf("."))}.geojson`);
                            fs.writeFileSync(geoFilePath, JSON.stringify(geoData, "", "\t"), { encoding: 'utf-8' });
                            if (writeFileCount === geojsonFiles.length-1) {
                                hasAllDataWriteed = true;
                                callback && callback(hasAllDataWriteed, outputDirPath)
                            } else {
                                writeFileCount++;
                            }
                        });
                    })(index);
                });

            }
            findJsonFile(filesPath);
        } catch (error) {
            console.error(error);
        }
        
        return hasAllDataWriteed
    },

    /**
     * // 集成geojson、shp格式数据，文件夹遍历
     * @param {*} fileOptions {filesPath,projObj,outpath}
     * @param {*} callback 
     * @returns 
     */
    readGeoJSONFilesInDir: async (fileOptions, callback)=> {
        const {dir,projObj} = fileOptions
        try {  
            await CommonUtil.traverseDirectory(dir, async (filePath)=>{
                // 子文件读取
                const geoData = await readGeoJSONObjByUrl(filePath, projObj); 
                fs.writeFileSync(filePath, JSON.stringify(geoData, "", "\t"), { encoding: 'utf-8' });
            });  
            console.log('All directories and files have been traversed.');  
            // 这里是遍历结束后的状态处理逻辑  
        } catch (err) {  
            console.error('Traversal failed:', err);  
        }  finally {
            callback && callback(true,dir);
        }
    },

    transCoordsByGeo: (geo, projObj) => {
        if (!geo) return;
        const { type, coordinates } = geo;
        const { fromWkid, toWkid, method } = projObj;
        let newGeo = Object.assign({
            coordinates: []
        }, {
            type,
        });
        let pointCoord;
        switch (type) {
            case "Point":
                pointCoord =
                    !(fromWkid && toWkid) ? transformService[method](coordinates[0], coordinates[1])
                        : transformService.proj4Transform(fromWkid, toWkid, coordinates);
                newGeo.coordinates = newGeo.coordinates.concat(pointCoord);
                break;

            case "LineString":
            case "MultiPoint":
                coordinates.forEach(coord => {
                    pointCoord =
                        !(fromWkid && toWkid) ? transformService[method](coord[0], coord[1])
                            : transformService.proj4Transform(fromWkid, toWkid, coord);
                    newGeo.coordinates.push(pointCoord);
                })
                break;

            case "Polygon":
            case "MultiLineString":
                coordinates.forEach((coords, i) => {
                    newGeo.coordinates.push([]);
                    coords.forEach((coord) => {
                        pointCoord =
                            !(fromWkid && toWkid) ? transformService[method](coord[0], coord[1])
                                : transformService.proj4Transform(fromWkid, toWkid, coord);
                        newGeo.coordinates[i].push(pointCoord);
                    });
                })
                break;


            case "MultiPolygon":
                coordinates.forEach((coords, i) => {
                    newGeo.coordinates.push([]);
                    coords.forEach((coord, j) => {
                        newGeo.coordinates[i].push([]);
                        coord.forEach((c) => {
                            pointCoord =
                                !(fromWkid && toWkid) ? transformService[method](c[0], c[1])
                                    : transformService.proj4Transform(fromWkid, toWkid, c);
                            newGeo.coordinates[i][j].push(pointCoord);
                        })

                    });
                })
                break;

            default:
                break;
        }
        return newGeo;
    },
}
module.exports = filesService;